﻿//RemoteMarketDataDrive.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Storages
{
	using System;
	using System.Collections.Generic;
	using System.IO;

	using StockSharp.Algo.Candles;
	using StockSharp.Algo.History.Hydra;
	using StockSharp.BusinessEntities;

	/// <summary>
	/// Удаленное хранилище маркет-данных, работающее через <see cref="RemoteStorageClient"/>.
	/// </summary>
	public class RemoteMarketDataDrive : BaseMarketDataDrive
	{
		private sealed class CacheableDrive : IMarketDataStorageDrive
		{
			private readonly IMarketDataStorageDrive _cacheDrive;
			private readonly IMarketDataStorageDrive _remoteDrive;

			public CacheableDrive(IMarketDataStorageDrive cacheDrive, IMarketDataStorageDrive remoteDrive)
			{
				if (remoteDrive == null)
					throw new ArgumentNullException("remoteDrive");

				_cacheDrive = cacheDrive;
				_remoteDrive = remoteDrive;
			}

			IEnumerable<DateTime> IMarketDataStorageDrive.Dates
			{
				get { return _remoteDrive.Dates; }
			}

			void IMarketDataStorageDrive.ClearDatesCache()
			{
				DoWithCache(c => c.ClearDatesCache());

				_remoteDrive.ClearDatesCache();
			}

			void IMarketDataStorageDrive.Delete(DateTime date)
			{
				DoWithCache(c => c.Delete(date));

				_remoteDrive.Delete(date);
			}

			void IMarketDataStorageDrive.Save(DateTime date, byte[] rawData)
			{
				_remoteDrive.Save(date, rawData);

				DoWithCache(c => c.Save(date, rawData));
			}

			byte[] IMarketDataStorageDrive.Load(DateTime date)
			{
				byte[] rawData;

				if (_cacheDrive != null)
				{
					rawData = _cacheDrive.Load(date);

					if (rawData != null)
						return rawData;
				}

				rawData = _remoteDrive.Load(date);

				DoWithCache(c => c.Save(date, rawData));
				return rawData;
			}

			void IMarketDataStorageDrive.SaveStream(DateTime date, Stream rawData)
			{
				_remoteDrive.SaveStream(date, rawData);

				DoWithCache(c => c.SaveStream(date, rawData));
			}

			Stream IMarketDataStorageDrive.LoadStream(DateTime date)
			{
				Stream rawData;

				if (_cacheDrive != null)
				{
					rawData = _cacheDrive.LoadStream(date);

					if (rawData != null)
						return rawData;
				}

				rawData = _remoteDrive.LoadStream(date);

				DoWithCache(c =>
				{
					c.SaveStream(date, rawData);
					rawData.Position = 0;
				});
				
				return rawData;
			}

			private void DoWithCache(Action<IMarketDataStorageDrive> action)
			{
				if (action == null)
					throw new ArgumentNullException("action");

				if (_cacheDrive != null)
					action(_cacheDrive);
			}
		}

		/// <summary>
		/// Создать <see cref="RemoteMarketDataDrive"/>.
		/// </summary>
		public RemoteMarketDataDrive()
			: this(new RemoteStorageClient())
		{
		}

		/// <summary>
		/// Создать <see cref="RemoteMarketDataDrive"/>.
		/// </summary>
		/// <param name="client">Клиент для доступа к серверу истории <see cref="IRemoteStorage"/>.</param>
		public RemoteMarketDataDrive(RemoteStorageClient client)
		{
			Client = client;
		}

		private RemoteStorageClient _client;

		/// <summary>
		/// Клиент для доступа к серверу истории <see cref="IRemoteStorage"/>.
		/// </summary>
		public RemoteStorageClient Client
		{
			get { return _client; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_client = value;
			}
		}

		/// <summary>
		/// Хранилище, используемое для кэширования скачиваемой с удаленного хранилища информации. По-умолчанию равно null, что значит кэш выключен.
		/// </summary>
		public IMarketDataDrive CacheDrive { get; set; }

		/// <summary>
		/// Получить для инструмента доступные типы свечек с параметрами.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <returns>Доступные типы свечек с параметрами.</returns>
		public override IEnumerable<Tuple<Type, object[]>> GetCandleTypes(Security security)
		{
			return Client.GetCandleTypes(security);
		}

		/// <summary>
		/// Создать хранилище для <see cref="IMarketDataStorage"/>.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <param name="dataType">Тип маркет-данных.</param>
		/// <param name="arg">Параметр, ассоциированный с типом <paramref name="dataType"/>. Например, <see cref="Candle.Arg"/>.</param>
		/// <returns>Хранилище для <see cref="IMarketDataStorage"/>.</returns>
		public override IMarketDataStorageDrive GetStorageDrive(Security security, Type dataType, object arg)
		{
			return new CacheableDrive(
				CacheDrive == null ? null : CacheDrive.GetStorageDrive(security, dataType, arg),
				Client.GetRemoteStorage(security, dataType, arg));
		}
	}
}